rsection{Linux 驱动准备}\label{linux-ux9a71ux52a8ux51c6ux5907}

\begin{itemize}
\tightlist
\item
  \protect\hyperlink{ioux4f53ux7cfbux7ed3ux6784}{I/O体系结构}
\item
  \protect\hyperlink{ux786cux4ef6ux8bbeux5907ux8fdeux63a5ux5230ux7cfbux7edf}{硬件设备连接到系统}

  \begin{itemize}
  \tightlist
  \item
    \protect\hyperlink{ux786cux4ef6ux5c42ux9762ux7684ux8fdeux63a5}{硬件层面的连接}
  \item
    \protect\hyperlink{ux4e0eux5916ux8bbeux901aux4fe1}{与外设通信}
  \end{itemize}
\item
  \protect\hyperlink{ux8bbeux5907ux6570ux636eux5e93}{设备数据库}

  \begin{itemize}
  \tightlist
  \item
    \protect\hyperlink{ux901aux7528ux6570ux636eux5e93ux6570ux636eux7ed3ux6784}{通用数据库数据结构}
  \item
    \protect\hyperlink{kobj_mapux51fdux6570}{kobj\_map函数}
  \item
    \protect\hyperlink{ux5b57ux7b26ux8bbeux5907ux76f8ux5173ux7684ux6570ux636eux5e93ux6570ux636eux7ed3ux6784}{字符设备相关的数据库数据结构}
  \end{itemize}
\item
  \protect\hyperlink{kobj}{kobj}

  \begin{itemize}
  \tightlist
  \item
    \protect\hyperlink{kobject}{kobject}
  \item
    \protect\hyperlink{kset}{kset}
  \item
    \protect\hyperlink{kobj_type}{kobj\_type}
  \item
    \protect\hyperlink{kobjectux76f8ux5173ux7684ux51fdux6570}{kobject相关的函数}
  \end{itemize}
\end{itemize}

\hypertarget{ioux4f53ux7cfbux7ed3ux6784}{\subsection{I/O体系结构}\label{ioux4f53ux7cfbux7ed3ux6784}}

处理外设IO的时候必须考虑的三个问题：

\begin{enumerate}
\def\labelenumi{\arabic{enumi}.}
\item
  如何对硬件设备进行寻址？（我怎么才能找到设备？）
\item
  如何设计上层与设备交互的方法？
\item
  如何管理并上报这些硬件设备？
\end{enumerate}

与外设的通信是一种层次化的通信

\begin{figure}
\centering
\includegraphics{/home/wu/code/learning/pictures/IO_layer_19_11_23_1.png}
\caption{IO\_layer}
\end{figure}

看得出来，与外设的通信是通过VFS层进行的。那么肯定需要支持一堆VFS的操作
比如打开关闭之类的操作。

这个设计方案回答了一部分上面三个问题中的2和3。应用程序可以通过VFS提供的标准操作来与硬件设备进行交互而从最终用户的角度来说管理这些特殊的文件就可以管理（至少是内核允许的用户层管理）这些硬件。

\hypertarget{ux786cux4ef6ux8bbeux5907ux8fdeux63a5ux5230ux7cfbux7edf}{\subsection{硬件设备连接到系统}\label{ux786cux4ef6ux8bbeux5907ux8fdeux63a5ux5230ux7cfbux7edf}}

\hypertarget{ux786cux4ef6ux5c42ux9762ux7684ux8fdeux63a5}{\subsubsection{硬件层面的连接}\label{ux786cux4ef6ux5c42ux9762ux7684ux8fdeux63a5}}

主要靠总线，就目前而言外设主要靠PCI总线而内存靠内存总线。

\hypertarget{ux4e0eux5916ux8bbeux901aux4fe1}{\subsubsection{与外设通信}\label{ux4e0eux5916ux8bbeux901aux4fe1}}

有如下几种交互的方式：

\begin{enumerate}
\def\labelenumi{\arabic{enumi}.}
\item
  I/O端口:
  处理器维持了一个独立的虚拟地址空间（CPU的物理地址线也有一部分被占用），通过特殊的IO指令与外设交互。
\item
  I/O内存映射：CPU提供了这样的功能，将内存区映射到外设的端口上（GPU应该这样），内核就可以通过通用的一些指令来处理这些外设。PCI总线也会这样搞，这肯定需要内核提供内存。内核除了支持PCI这样弄之外还提供了抽象层对I/O映射进行处理。
\item
  轮询与中断：这没啥可说的
\item
  通过总线控制设备的访问：总线控制设备肯定提供了很多高级的功能诸如设备描述表之类的东西来描述连接到总线上的设备。通过总线控制设备与设备交互肯定更加方便。
\end{enumerate}

\hypertarget{ux8bbeux5907ux6570ux636eux5e93}{\subsection{设备数据库}\label{ux8bbeux5907ux6570ux636eux5e93}}

内核如何快速知道目前有那些字符与块设备？当然需要建立一个数据库。
可以想到内核使用了散列表来处理快速这一需求，通过若干的指针寻找到要找的设备，所以这一块比较无聊。

\hypertarget{ux901aux7528ux6570ux636eux5e93ux6570ux636eux7ed3ux6784}{\subsubsection{通用数据库数据结构}\label{ux901aux7528ux6570ux636eux5e93ux6570ux636eux7ed3ux6784}}

内核使用cdev\_map来表示字符设备的散列表，bdev\_map表示块设备的散列表。内核对这俩的定义是这样的。

\begin{Shaded}
\begin{Highlighting}[]
\CommentTok{//static 说明你不能随便乱搞这些数据结构而只能通过接口}
\DataTypeTok{static} \KeywordTok{struct}\NormalTok{ kobj_map *cdev_map;}

\DataTypeTok{static} \KeywordTok{struct}\NormalTok{ kobj_map *bdev_map;}
\end{Highlighting}
\end{Shaded}

看来是kobj\_map数据结构的指针，指向的东西搞了半天是个高级数组。
想象一下kobj\_map肯定包括指向的设备，主设备号被拉去做散列，那么次设备号就被写在结构体里面了。

\begin{Shaded}
\begin{Highlighting}[]
\KeywordTok{struct}\NormalTok{ kobj_map \{}
    \KeywordTok{struct}\NormalTok{ probe \{}
        \CommentTok{//形成单链表}
        \KeywordTok{struct}\NormalTok{ probe *next;}
        \CommentTok{//意外，主次设备号全写进来了}
\NormalTok{        dev_t dev;}
        \CommentTok{//从设备号的范围。难道通过这个还可以处理到从设备？}
        \DataTypeTok{unsigned} \DataTypeTok{long}\NormalTok{ range;}
        \CommentTok{//设备驱动模块}
        \KeywordTok{struct}\NormalTok{ module *owner;}
\NormalTok{        kobj_probe_t *get;}
        \DataTypeTok{int}\NormalTok{ (*lock)(dev_t, }\DataTypeTok{void}\NormalTok{ *);}
        \CommentTok{//其实就是指向了各自设备对应的结构体}
        \DataTypeTok{void}\NormalTok{ *data;}
\NormalTok{    \} *probes[}\DecValTok{255}\NormalTok{];}\CommentTok{//主设备号%255就是散列算法。。。。。。}
    \CommentTok{//锁子，锁住了整个散列表}
    \KeywordTok{struct}\NormalTok{ mutex *lock;}
\NormalTok{\};}
\end{Highlighting}
\end{Shaded}

\hypertarget{kobj_mapux51fdux6570}{\subsubsection{kobj\_map函数}\label{kobj_mapux51fdux6570}}

这个函数就是注册一下设备号以及设备号与实体的关联。

\begin{Shaded}
\begin{Highlighting}[]

\DataTypeTok{int}\NormalTok{ kobj_map(}\KeywordTok{struct}\NormalTok{ kobj_map *domain, dev_t dev, }\DataTypeTok{unsigned} \DataTypeTok{long}\NormalTok{ range,}
         \KeywordTok{struct}\NormalTok{ module *module, kobj_probe_t *probe,}
         \DataTypeTok{int}\NormalTok{ (*lock)(dev_t, }\DataTypeTok{void}\NormalTok{ *), }\DataTypeTok{void}\NormalTok{ *data)}
\NormalTok{\{}
    \DataTypeTok{unsigned}\NormalTok{ n = MAJOR(dev + range - }\DecValTok{1}\NormalTok{) - MAJOR(dev) + }\DecValTok{1}\NormalTok{;}
    \DataTypeTok{unsigned}\NormalTok{ index = MAJOR(dev);}
    \DataTypeTok{unsigned}\NormalTok{ i;}
    \KeywordTok{struct}\NormalTok{ probe *p;}

    \ControlFlowTok{if}\NormalTok{ (n > }\DecValTok{255}\NormalTok{)}
\NormalTok{        n = }\DecValTok{255}\NormalTok{;}

\NormalTok{    p = kmalloc(}\KeywordTok{sizeof}\NormalTok{(}\KeywordTok{struct}\NormalTok{ probe) * n, GFP_KERNEL);}

    \ControlFlowTok{if}\NormalTok{ (p == NULL)}
        \ControlFlowTok{return}\NormalTok{ -ENOMEM;}

    \ControlFlowTok{for}\NormalTok{ (i = }\DecValTok{0}\NormalTok{; i < n; i++, p++) \{}
\NormalTok{        p->owner = module;}
\NormalTok{        p->get = probe;}
\NormalTok{        p->lock = lock;}
\NormalTok{        p->dev = dev;}
\NormalTok{        p->range = range;}
\NormalTok{        p->data = data;}
\NormalTok{    \}}
\NormalTok{    mutex_lock(domain->lock);}
    \CommentTok{//串表}
    \ControlFlowTok{for}\NormalTok{ (i = }\DecValTok{0}\NormalTok{, p -= n; i < n; i++, p++, index++) \{}
        \KeywordTok{struct}\NormalTok{ probe **s = &domain->probes[index % }\DecValTok{255}\NormalTok{];}
        \ControlFlowTok{while}\NormalTok{ (*s && (*s)->range < range)}
\NormalTok{            s = &(*s)->next;}
\NormalTok{        p->next = *s;}
\NormalTok{        *s = p;}
\NormalTok{    \}}
\NormalTok{    mutex_unlock(domain->lock);}
    \ControlFlowTok{return} \DecValTok{0}\NormalTok{;}
\NormalTok{\}}

\end{Highlighting}
\end{Shaded}

\hypertarget{ux5b57ux7b26ux8bbeux5907ux76f8ux5173ux7684ux6570ux636eux5e93ux6570ux636eux7ed3ux6784}{\subsubsection{字符设备相关的数据库数据结构}\label{ux5b57ux7b26ux8bbeux5907ux76f8ux5173ux7684ux6570ux636eux5e93ux6570ux636eux7ed3ux6784}}

除了上面的那个散列表之外，为了管理字符设备的从设备号范围。内核增加了一个数据库：char\_device\_struct

\begin{Shaded}
\begin{Highlighting}[]
\DataTypeTok{static} \KeywordTok{struct}\NormalTok{ char_device_struct \{}
    \CommentTok{//形成链表}
    \KeywordTok{struct}\NormalTok{ char_device_struct *next;}
    \DataTypeTok{unsigned} \DataTypeTok{int}\NormalTok{ major;}
    \CommentTok{//最小从设备号}
    \DataTypeTok{unsigned} \DataTypeTok{int}\NormalTok{ baseminor;}
    \CommentTok{//多少个从设备号}
    \DataTypeTok{int}\NormalTok{ minorct;}
    \DataTypeTok{char}\NormalTok{ name[}\DecValTok{64}\NormalTok{];}
    \KeywordTok{struct}\NormalTok{ cdev *cdev;      }\CommentTok{/* will die: 如果这个结构体也能找到字符设备的结构体，那么不就和kobj_map散列表的功能重了？所以will die*/} 
\NormalTok{\} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];}
\end{Highlighting}
\end{Shaded}

对于数据库的操作很直接，所以不加赘述。块设备没有第二个数据库所以略过不表。

\hypertarget{kobj}{\subsection{kobj}\label{kobj}}

驱动相关的内核代码中出现了很多kobject、kset之类的东西。我们来解析一下这个东西是用来干啥的。

内核的驱动层存在大量的对象，为了方便管理这些对象并抽象一些可能共同存在的属性或者操作或者接口，有必要建立一个专门的kobject。

内核不仅仅是在驱动层使用了kobject，在其他目录下也有。

\hypertarget{kobject}{\subsubsection{kobject}\label{kobject}}

\begin{Shaded}
\begin{Highlighting}[]

\KeywordTok{struct}\NormalTok{ kobject \{}
    \CommentTok{//对象的名字，导出到sys文件系统可用此}
    \DataTypeTok{const} \DataTypeTok{char}\NormalTok{      *name;}
    \CommentTok{//引用计数，只有一个atomic_t类型的成员用来计数}
    \KeywordTok{struct}\NormalTok{ kref     kref;}
    \CommentTok{//链表}
    \KeywordTok{struct}\NormalTok{ list_head    entry;}
    \CommentTok{//父kobject}
    \KeywordTok{struct}\NormalTok{ kobject      *parent;}
    \CommentTok{//所属的kset}
    \KeywordTok{struct}\NormalTok{ kset     *kset;}
    \CommentTok{//与此kobject相关联的类型以及操作函数指针等，不同的kobject可能共用ktype所以是个指针}
    \KeywordTok{struct}\NormalTok{ kobj_type    *ktype;}
    \CommentTok{//关联的sysfs}
    \KeywordTok{struct}\NormalTok{ sysfs_dirent *sd;}
    \CommentTok{//是否注册了}
    \DataTypeTok{unsigned} \DataTypeTok{int}\NormalTok{ state_initialized:}\DecValTok{1}\NormalTok{;}
    \CommentTok{//是否导出到sysfs}
    \DataTypeTok{unsigned} \DataTypeTok{int}\NormalTok{ state_in_sysfs:}\DecValTok{1}\NormalTok{;}
    \CommentTok{//kobject添加}
    \DataTypeTok{unsigned} \DataTypeTok{int}\NormalTok{ state_add_uevent_sent:}\DecValTok{1}\NormalTok{;}
    \CommentTok{//kobject移除}
    \DataTypeTok{unsigned} \DataTypeTok{int}\NormalTok{ state_remove_uevent_sent:}\DecValTok{1}\NormalTok{;}
\NormalTok{\};}
\end{Highlighting}
\end{Shaded}

通过观察可以发现，kobject彼此之间保留了最小的共同特性子集而把操作类似的东西留给了ktype之类的结构。

\hypertarget{kset}{\subsubsection{kset}\label{kset}}

kset是一种特殊的kobject，可以容纳多种类型的kobject（话说既然是ktype这个指针区别的话这个功能意义不大啊）

\begin{Shaded}
\begin{Highlighting}[]
\KeywordTok{struct}\NormalTok{ kset \{}
    \CommentTok{//链表，表示kobject的集合}
    \KeywordTok{struct}\NormalTok{ list_head list;}
    \CommentTok{//链表的锁}
\NormalTok{    spinlock_t list_lock;}
    \CommentTok{//kset本身也是一个kobject，所以也有}
    \KeywordTok{struct}\NormalTok{ kobject kobj;}
    \CommentTok{//uevent相关的内容}
    \KeywordTok{struct}\NormalTok{ kset_uevent_ops *uevent_ops;}
\NormalTok{\};}
\end{Highlighting}
\end{Shaded}

\hypertarget{kobj_type}{\subsubsection{kobj\_type}\label{kobj_type}}

kobj\_type，给出了更进一步的kobject信息。

\begin{Shaded}
\begin{Highlighting}[]

\KeywordTok{struct}\NormalTok{ kobj_type \{}
    \CommentTok{//释放kobj时候的操作}
    \DataTypeTok{void}\NormalTok{ (*release)(}\KeywordTok{struct}\NormalTok{ kobject *kobj);}
    \KeywordTok{struct}\NormalTok{ sysfs_ops *sysfs_ops;}
    \KeywordTok{struct}\NormalTok{ attribute **default_attrs;}
\NormalTok{\};}


\KeywordTok{struct}\NormalTok{ sysfs_ops \{}
    \DataTypeTok{ssize_t}\NormalTok{ (*show)(}\KeywordTok{struct}\NormalTok{ kobject *, }\KeywordTok{struct}\NormalTok{ attribute *,}\DataTypeTok{char}\NormalTok{ *);}
    \DataTypeTok{ssize_t}\NormalTok{ (*store)(}\KeywordTok{struct}\NormalTok{ kobject *,}\KeywordTok{struct}\NormalTok{ attribute *,}\DataTypeTok{const} \DataTypeTok{char}\NormalTok{ *, }\DataTypeTok{size_t}\NormalTok{);}
\NormalTok{\};}


\CommentTok{//暂时还没有看到有啥用}
\KeywordTok{struct}\NormalTok{ attribute \{}
    \DataTypeTok{const} \DataTypeTok{char}\NormalTok{      *name;}
    \KeywordTok{struct}\NormalTok{ module       *owner;}
\NormalTok{    mode_t          mode;}
\NormalTok{\};}
\end{Highlighting}
\end{Shaded}

\hypertarget{kobjectux76f8ux5173ux7684ux51fdux6570}{\subsubsection{kobject相关的函数}\label{kobjectux76f8ux5173ux7684ux51fdux6570}}

\begin{enumerate}
\def\labelenumi{\arabic{enumi}.}
\item
  kobject\_get、kobject\_put函数增加和减少引用计数。
\item
  kobject\_init:初始化对象。
\end{enumerate}

\begin{Shaded}
\begin{Highlighting}[]
\DataTypeTok{void}\NormalTok{ kobject_init(}\KeywordTok{struct}\NormalTok{ kobject *kobj, }\KeywordTok{struct}\NormalTok{ kobj_type *ktype)}
\NormalTok{\{}
    \DataTypeTok{char}\NormalTok{ *err_str;}

    \ControlFlowTok{if}\NormalTok{ (!kobj) \{}
\NormalTok{        err_str = }\StringTok{"invalid kobject pointer!"}\NormalTok{;}
        \ControlFlowTok{goto}\NormalTok{ error;}
\NormalTok{    \}}
    \ControlFlowTok{if}\NormalTok{ (!ktype) \{}
\NormalTok{        err_str = }\StringTok{"must have a ktype to be initialized properly!}\SpecialCharTok{\textbackslash{}n}\StringTok{"}\NormalTok{;}
        \ControlFlowTok{goto}\NormalTok{ error;}
\NormalTok{    \}}
    \CommentTok{//不允许重复初始化}
    \ControlFlowTok{if}\NormalTok{ (kobj->state_initialized) \{}
        \CommentTok{/* do not error out as sometimes we can recover */}
\NormalTok{        printk(KERN_ERR }\StringTok{"kobject (%p): tried to init an initialized "}
               \StringTok{"object, something is seriously wrong.}\SpecialCharTok{\textbackslash{}n}\StringTok{"}\NormalTok{, kobj);}
\NormalTok{        dump_stack();}
\NormalTok{    \}}
    
\NormalTok{    检查完毕后调用kobject_init_internal函数来初始化kobj}
\NormalTok{    kobject_init_internal(kobj);}
\NormalTok{    kobj->ktype = ktype;}
    \ControlFlowTok{return}\NormalTok{;}

\NormalTok{error:}
\NormalTok{    printk(KERN_ERR }\StringTok{"kobject (%p): %s}\SpecialCharTok{\textbackslash{}n}\StringTok{"}\NormalTok{, kobj, err_str);}
\NormalTok{    dump_stack();}
\NormalTok{\}}


\DataTypeTok{static} \DataTypeTok{void}\NormalTok{ kobject_init_internal(}\KeywordTok{struct}\NormalTok{ kobject *kobj)}
\NormalTok{\{}
    \ControlFlowTok{if}\NormalTok{ (!kobj)}
        \ControlFlowTok{return}\NormalTok{;}
\NormalTok{    kref_init(&kobj->kref);}
\NormalTok{    INIT_LIST_HEAD(&kobj->entry);}
\NormalTok{    kobj->state_in_sysfs = }\DecValTok{0}\NormalTok{;}
\NormalTok{    kobj->state_add_uevent_sent = }\DecValTok{0}\NormalTok{;}
\NormalTok{    kobj->state_remove_uevent_sent = }\DecValTok{0}\NormalTok{;}
    \CommentTok{//标明这个kobject已经初始化过了}
\NormalTok{    kobj->state_initialized = }\DecValTok{1}\NormalTok{;}
\NormalTok{\}}
\end{Highlighting}
\end{Shaded}

问题是kobject彼此之间建立联系的过程。

\begin{enumerate}
\def\labelenumi{\arabic{enumi}.}
\setcounter{enumi}{2}
\tightlist
\item
  kobject\_add函数。
\end{enumerate}

\begin{Shaded}
\begin{Highlighting}[]
\DataTypeTok{int}\NormalTok{ kobject_add(}\KeywordTok{struct}\NormalTok{ kobject *kobj, }\KeywordTok{struct}\NormalTok{ kobject *parent,}
        \DataTypeTok{const} \DataTypeTok{char}\NormalTok{ *fmt, ...)}
\NormalTok{\{}
\NormalTok{    va_list args;}
    \DataTypeTok{int}\NormalTok{ retval;}

    \ControlFlowTok{if}\NormalTok{ (!kobj)}
        \ControlFlowTok{return}\NormalTok{ -EINVAL;}

    \ControlFlowTok{if}\NormalTok{ (!kobj->state_initialized) \{}
\NormalTok{        printk(KERN_ERR }\StringTok{"kobject '%s' (%p): tried to add an "}
               \StringTok{"uninitialized object, something is seriously wrong.}\SpecialCharTok{\textbackslash{}n}\StringTok{"}\NormalTok{,}
\NormalTok{               kobject_name(kobj), kobj);}
\NormalTok{        dump_stack();}
        \ControlFlowTok{return}\NormalTok{ -EINVAL;}
\NormalTok{    \}}
\NormalTok{    va_start(args, fmt);}
    \CommentTok{//这个函数最终会设定名字（必须），加入kset（如果kobject设定了），导出对象到sysfs（vfs再说）}
\NormalTok{    retval = kobject_add_varg(kobj, parent, fmt, args);}
\NormalTok{    va_end(args);}

    \ControlFlowTok{return}\NormalTok{ retval;}
\NormalTok{\}}
\end{Highlighting}
\end{Shaded}

